<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2018/9/5 0005
 * Time: 上午 10:51
 */
//require_once __DIR__ . '/aop/AopEncrypt.php';
class AlipayGather
{
    public $app_id;
    public $method;
    public $format = 'json';
    public $return_url;
    public $charset;
    public $sign_type;
    public $timestamp;
    public $version;
    public $notify_url;
    public $biz_content;
    public $merchant_private_key;
    public $alipay_public_key;
    public $gateway_url;
    public $postCharset = 'UTF-8';
    private $RESPONSE_SUFFIX = "_response";
    private $ERROR_RESPONSE = "error_response";

    private $SIGN_NODE_NAME = "sign";

    public function __construct()
    {
        include_once  BASE_DATA_PATH.'/config/my_conf.php';

        $alipay_conf = $my_conf['ali_pay'];
        if (empty($alipay_conf)){
            throw new Exception("读取Alipay配置失败!");
        }

        $this->app_id = $alipay_conf['app_id'];
        $this->merchant_private_key = $alipay_conf['merchant_private_key'];
        $this->notify_url = $alipay_conf['notify_url'];
        $this->return_url = $alipay_conf['return_url'];
        $this->charset = $alipay_conf['charset'];
        $this->sign_type = $alipay_conf['sign_type'];
        $this->alipay_public_key = $alipay_conf['alipay_public_key'];
        $this->version = '1.0';
        $this->gateway_url = $alipay_conf['gatewayUrl'];


        if (empty($this->app_id) || trim($this->app_id) == "") {
            throw new Exception("appid should not be NULL!");
        }
        if (empty($this->merchant_private_key) || trim($this->merchant_private_key) == "") {
            throw new Exception("private_key should not be NULL!");
        }
        if (empty($this->alipay_public_key) || trim($this->alipay_public_key) == "") {
            throw new Exception("alipay_public_key should not be NULL!");
        }
        if (empty($this->charset) || trim($this->charset) == "") {
            throw new Exception("charset should not be NULL!");
        }
        if (empty($this->gateway_url) || trim($this->gateway_url) == "") {
            throw new Exception("gateway_url should not be NULL!");
        }
    }

    /**
     * PC支付
     * @author: yanggang
     * @date: 2018/9/10 0010
     * @param $order_info
     * @param string $httpmethod
     * @return string|提交表单HTML文本
     */
    public function PCPay($order_info, $httpmethod = 'POST')
    {
        //商户订单号，商户网站订单系统中唯一订单号，必填
        $out_trade_no = $order_info['pay_sn'];

        //订单名称，必填
        $subject = $order_info['subject'];

        //付款金额，必填
        $total_amount = $order_info['api_pay_amount'];

        //商品描述，可空
        $body = '';

        //超时时间
        $timeout_express = "";

        //PC扫码支付的方式
        $qr_pay_mode = 2;

//        $this->method = 'alipay.trade.wap.pay';
        $this->method = 'alipay.trade.page.pay';

        $biz_content = array_filter([
            'out_trade_no' => $out_trade_no,
            'subject' => $subject,
            'total_amount' => $total_amount,
            'body' => $body,
            'timeout_express' => $timeout_express,
            'passback_params' => urlencode('order_type='.$order_info['order_type']),
//            'productCode' =>'QUICK_WAP_PAY',
            'product_code' => 'FAST_INSTANT_TRADE_PAY'
        ]);


        $this->biz_content = $biz_content;

        $params = $this->setBizContent();
        $preSignStr = $params['preSignStr'];
        $sys_params = $params['sys_params'];
        if ("GET" == $httpmethod) {

            //拼接GET请求串
            $requestUrl = $this->gateway_url . "?" . $preSignStr . "&sign=" . urlencode($sys_params["sign"]);
            return $requestUrl;
        } else {
            //拼接表单字符串
            return $this->buildRequestForm($sys_params);
        }
    }

    /**
     * 统一退款
     * @author: yanggang
     * @date: 2018/9/10 0010
     * @param $order_info
     * @return bool|mixed
     */
    public function refund_money($order_info)
    {
        $this->method = 'alipay.trade.refund';

        $out_trade_no = $order_info['pay_sn'];
        $order_amount = $order_info['order_amount'];
        $biz_content = array_filter([
            'out_trade_no' => $out_trade_no,
            'refund_amount' => $order_amount
        ]);
        $this->biz_content = $biz_content;

        $sys_params = [
            'app_id' => $this->app_id,
            'version' => $this->version,
            'format' => $this->format,
            'alipay_sdk' => 'alipay-sdk-php-20161101',
            'method' => $this->method,
            'charset' => $this->charset,
            'sign_type' => $this->sign_type,
            'timestamp' => date('Y-m-d H:i:s'),
            'biz_content' => json_encode($this->biz_content)
        ];

        $preSignStr = $this->getSignContent($sys_params);
        $sys_params['sign'] = $this->sign($preSignStr, $this->sign_type);


        //系统参数放入GET请求串
        $requestUrl = $this->gateway_url . "?";
        foreach ($sys_params as $sysParamKey => $sysParamValue) {
            $requestUrl .= "$sysParamKey=" . urlencode($sysParamValue) . "&";
        }
        $requestUrl = substr($requestUrl, 0, -1);

        //发起HTTP请求
        try {
            $resp = $this->curl($requestUrl, $sys_params);
        } catch (Exception $e) {

//            $this->logCommunicationError($sys_params["method"], $requestUrl, "HTTP_ERROR_" . $e->getCode(), $e->getMessage());
//            var_dump($sys_params['method'],$requestUrl,'HTTP_ERROR',$e->getCode(),$e->getMessage());
            return false;
        }

        //解析AOP返回结果
        $respWellFormed = false;

        $signData = null;
        $request = null;
        if ("json" == $this->format) {

            $respObject = json_decode($resp);
            if (null !== $respObject) {
                $respWellFormed = true;
                $signData = json_decode($resp,true);
            }
        }

        //返回的HTTP文本不是标准JSON或者XML，记下错误日志
        if (false === $respWellFormed) {
//            $this->logCommunicationError($sys_params["method"], $requestUrl, "HTTP_RESPONSE_NOT_WELL_FORMED", $resp);
            return false;
        }

        // 验签
        $sign_str = json_encode($signData['alipay_trade_refund_response'], JSON_UNESCAPED_UNICODE);
        $res = $this->verify($sign_str,$signData['sign'],null,$this->sign_type);

        if (!$res ||  !$signData['alipay_trade_refund_response']['code'] == 10000){
            return  false;
        }
        return true;

    }

    protected function setBizContent()
    {

        $sys_params = array_filter([
            'app_id' => $this->app_id,
            'version' => $this->version,
            'format' => $this->format,
            'alipay_sdk' => 'alipay-sdk-php-20161101',
            'notify_url' => $this->notify_url,
            'return_url' => $this->return_url,
            'method' => $this->method,
            'charset' => $this->charset,
            'sign_type' => $this->sign_type,
            'timestamp' => date('Y-m-d H:i:s'),
            'biz_content' => json_encode($this->biz_content)
        ]);
        $preSignStr = $this->getSignContent($sys_params);
        $sys_params['sign'] = $this->sign($preSignStr, $this->sign_type);

        return compact('preSignStr', 'sys_params');

    }

    public function getSignContent($params)
    {
        ksort($params);
        $stringToBeSigned = "";
        $i = 0;
        foreach ($params as $k => $v) {
            if (!empty($v) && "@" != substr($v, 0, 1)) {
                if ($i == 0) {
                    $stringToBeSigned .= "$k" . "=" . "$v";
                } else {
                    $stringToBeSigned .= "&" . "$k" . "=" . "$v";
                }
                $i++;
            }
        }

        unset ($k, $v);
        return $stringToBeSigned;
    }

    protected function sign($data, $signType = "RSA")
    {
        if (empty($this->rsaPrivateKeyFilePath)) {
            $priKey = $this->merchant_private_key;
            $res = "-----BEGIN RSA PRIVATE KEY-----\n" .
                wordwrap($priKey, 64, "\n", true) .
                "\n-----END RSA PRIVATE KEY-----";
        } else {
            $priKey = file_get_contents($this->rsaPrivateKeyFilePath);
            $res = openssl_get_privatekey($priKey);
        }

        ($res) or die('您使用的私钥格式错误，请检查RSA私钥配置');

        if ("RSA2" == $signType) {
            openssl_sign($data, $sign, $res, OPENSSL_ALGO_SHA256);
        } else {
            openssl_sign($data, $sign, $res);
        }

        if (!empty($this->rsaPrivateKeyFilePath)) {
            openssl_free_key($res);
        }
        $sign = base64_encode($sign);
        return $sign;
    }

    /**
     * 建立请求，以表单HTML形式构造（默认）
     * @param $para_temp 请求参数数组
     * @return 提交表单HTML文本
     */
    protected function buildRequestForm($para_temp)
    {

        $sHtml = "<form id='alipaysubmit' name='alipaysubmit' action='" . $this->gateway_url . "?charset=" . trim($this->postCharset) . "' method='POST'>";

        foreach ($para_temp as $key => $val) {
            if (!empty($val)) {
                //$val = $this->characet($val, $this->postCharset);
                $val = str_replace("'", "&apos;", $val);
                //$val = str_replace("\"","&quot;",$val);
                $sHtml .= "<input type='hidden' name='" . $key . "' value='" . $val . "'/>";
            }
        }

        //submit按钮控件请不要含有name属性
        $sHtml = $sHtml . "<input type='submit' value='ok' style='display:none;''></form>";

        $sHtml = $sHtml . "<script>document.forms['alipaysubmit'].submit();</script>";

        return $sHtml;
    }


    /**
     * 验签方法
     * @param $arr 验签支付宝返回的信息，使用支付宝公钥。
     * @return boolean
     */
    public function check($arr){
        $result = $this->rsaCheckV1($arr,null,$this->sign_type);
        return $result;
    }

    private function rsaCheckV1($params,$rsaPublicKeyFilePath, $signType='RSA')
    {
        $sign = $params['sign'];
        $params['sign_type'] = null;
        $params['sign'] = null;
        return $this->verify($this->getSignContent($params), $sign, $rsaPublicKeyFilePath,$signType);
    }

    private function verify($data, $sign, $rsaPublicKeyFilePath, $signType = 'RSA') {

        if(empty($rsaPublicKeyFilePath)){

            $pubKey= $this->alipay_public_key;
            $res = "-----BEGIN PUBLIC KEY-----\n" .
                wordwrap($pubKey, 64, "\n", true) .
                "\n-----END PUBLIC KEY-----";
        }else {
            //读取公钥文件
            $pubKey = file_get_contents($rsaPublicKeyFilePath);
            //转换为openssl格式密钥
            $res = openssl_get_publickey($pubKey);
        }
        ($res) or die('支付宝RSA公钥错误。请检查公钥文件格式是否正确');

        //调用openssl内置方法验签，返回bool值

        $sign = base64_decode($sign);
        if ("RSA2" == $signType) {
            $result = (bool)openssl_verify($data, $sign, $res, OPENSSL_ALGO_SHA256);
        } else {
            $result = (bool)openssl_verify($data,$sign, $res);
        }
        if(!empty($rsaPublicKeyFilePath)) {
            //释放资源
            openssl_free_key($res);
        }

        return $result;
    }


    protected function curl($url, $postFields = null) {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_FAILONERROR, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

        $postBodyString = "";
        $encodeArray = Array();
        $postMultipart = false;


        if (is_array($postFields) && 0 < count($postFields)) {

            foreach ($postFields as $k => $v) {
                if ("@" != substr($v, 0, 1)) //判断是不是文件上传
                {

                    $postBodyString .= "$k=" . urlencode($v) . "&";
                    $encodeArray[$k] = $v;
                } else //文件上传用multipart/form-data，否则用www-form-urlencoded
                {
                    $postMultipart = true;
                    $encodeArray[$k] = new \CURLFile(substr($v, 1));
                }

            }
            unset ($k, $v);
            curl_setopt($ch, CURLOPT_POST, true);
            if ($postMultipart) {
                curl_setopt($ch, CURLOPT_POSTFIELDS, $encodeArray);
            } else {
                curl_setopt($ch, CURLOPT_POSTFIELDS, substr($postBodyString, 0, -1));
            }
        }

        if ($postMultipart) {

            $headers = array('content-type: multipart/form-data;charset=' . $this->postCharset . ';boundary=' . $this->getMillisecond());
        } else {

            $headers = array('content-type: application/x-www-form-urlencoded;charset=' . $this->postCharset);
        }
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);




        $reponse = curl_exec($ch);

        if (curl_errno($ch)) {

            throw new Exception(curl_error($ch), 0);
        } else {
            $httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            if (200 !== $httpStatusCode) {
                throw new Exception($reponse, $httpStatusCode);
            }
        }

        curl_close($ch);
        return $reponse;
    }

    protected function getMillisecond() {
        list($s1, $s2) = explode(' ', microtime());
        return (float)sprintf('%.0f', (floatval($s1) + floatval($s2)) * 1000);
    }

}